import { cached } from '@/ayaka'
import { CalcCreepCost } from '@/complex-types/add'

/**
 * 判断一个房间是否是过道房间
 */
export const isHighWayRoom = cached((roomName: string) => {
    return roomName.includes('0')
})

/**
 * 判断一个房是否是中央九房，一个房的是区块最中心的九个房则是中央九房
 * @param roomName
 * @returns boolean
 */
export const isCenterNineRoom = cached((roomName: string) => {
    const [_, x, y] = (roomName.match(/[EW](\d+)[SN](\d+)/) || []).map((num) => Number(num) % 10)
    return x >= 4 && x <= 6 && y >= 4 && y <= 6
})

/**
 * 判断一个房是否是中央房，一个房的是区块最中心的房则是中央房
 * @param roomName
 * @returns boolean
 */
export const isCenterRoom = cached((roomName: string) => {
    const [_, x, y] = (roomName.match(/[EW](\d+)[SN](\d+)/) || []).map((num) => Number(num) % 10)
    return x === 5 && y === 5
})

/**
 * 计算爬孵化需要的能量
 * @param body 身体部件
 */
export const calcCreepCost = <T extends BodyPartConstant[]>(body: BodyPartConstant[]) => {
    return body.reduce((total, part) => total + BODYPART_COST[part], 0) as CalcCreepCost<T> extends 0
        ? number
        : CalcCreepCost<T>
}

/**
 * 特定格式的身体部件数组
 */
export type BodyPartArrayType =
    | BodyPartConstant[]
    | [BodyPartConstant, number]
    | [BodyPartArrayType, number]
    | BodyPartArrayType[]

/**
 * 将特定格式的身体部件展开，例如：
 * 将 [[MOVE,2],[WORK,1]] 展开成 [MOVE,MOVE,WORK] 的形式
 * 详细用法请见测试用例
 */
export const flatBodyParts = cached((array: BodyPartArrayType): BodyPartConstant[] => {
    // 如果数组第二个元素是数字
    const first = array[0]
    const multiple = array[1]
    let result: BodyPartConstant[] = []
    if (typeof multiple === 'number') {
        for (let i = 0; i < multiple; i++) {
            result = result.concat(parseBodyParts(first))
        }
    } else {
        array.forEach((r) => {
            // if (typeof r === 'number') return
            // @ts-ignore r 不可能是数字
            result = result.concat(parseBodyParts(r))
        })
    }
    return result
})

/**
 * 解析特定格式的身体部件中包含哪些部件
 */
export const getBodyPartTypes = cached((array: BodyPartArrayType): BodyPartConstant[] => {
    // 如果数组第二个元素是数字
    const first = array[0]
    const multiple = array[1]
    const result: BodyPartConstant[] = []
    if (typeof multiple === 'number') {
        if (first instanceof Array) {
            result.push(...getBodyPartTypes(first))
        } else {
            result.push(first)
        }
    } else {
        array.forEach((r) => {
            // if (typeof r === 'number') return
            if (typeof r === 'string') r = [r, 1]
            // @ts-ignore r 不可能是数字
            result.push(...getBodyPartTypes(r))
        })
    }
    return Array.from(new Set(result))
})

export const parseBodyParts = cached((array: BodyPartConstant | BodyPartArrayType) => {
    if (typeof array === 'string') return [array]
    return flatBodyParts(array)
})

/**
 * 身体部件归类
 */
export type BodyPartSort = Record<BodyPartConstant, number>

/**
 * 分类身体部件
 * @param bodyParts 身体部件
 */
export const classifyBodyParts = (bodyParts: BodyPartConstant[]) => {
    const bodyPartsObj: BodyPartSort = {
        move: 0,
        work: 0,
        attack: 0,
        ranged_attack: 0,
        heal: 0,
        claim: 0,
        tough: 0,
        carry: 0,
    }
    bodyParts.forEach((type) => {
        if (!bodyPartsObj[type]) bodyPartsObj[type] = 1
        else bodyPartsObj[type]++
    })
    return bodyPartsObj
}

/**
 * 找到部件分类对象中最多的部件，当move和另一部件均为最多时，选择另一部件
 * @param bodyPartsObj 部件分类对象
 */
export const findMostPart = (bodyPartsObj: BodyPartSort): BodyPartConstant | null => {
    let mostPart: BodyPartConstant | null = null
    let mostPartNumber = 2
    for (const type in bodyPartsObj) {
        const number = bodyPartsObj[type as BodyPartConstant]
        if (!number) continue
        if (number > mostPartNumber || (number === mostPartNumber && type !== 'move')) {
            mostPart = type as BodyPartConstant
            mostPartNumber = number
        }
    }
    return mostPart
}

/**
 * 自适应体型
 * @param bodyParts 身体部件
 * @param maxEnergy 最大能量
 */
export const adaptiveBody = (bodyParts: BodyPartConstant[], maxEnergy: number): BodyPartConstant[] => {
    // 不要修改原数组！
    bodyParts = [...bodyParts]

    const bodyPartsObj = classifyBodyParts(bodyParts)
    // 先统计其他部件和move部件比值
    let moveNumber = bodyPartsObj['move'] || 0
    let otherNumber = bodyParts.length - moveNumber
    // 处理没有move部件的爬，比如某些挖过道的爬
    let ratio = moveNumber ? Math.ceil(otherNumber / moveNumber) : 10000
    // 处理只有move部件的爬
    ratio = Math.max(1, ratio)
    while (calcCreepCost(bodyParts) > maxEnergy) {
        // 这里有个隐藏bug，某个部件可能会被减没，不过一般没事，摆烂了
        if (maxEnergy < 300) return bodyParts
        const mostPart = findMostPart(bodyPartsObj)
        if (!mostPart) {
            return bodyParts
        }

        const index = bodyParts.indexOf(mostPart)
        if (~index) {
            bodyParts.splice(index, 1)
            const mostPartRecord = bodyPartsObj[mostPart]
            if (mostPartRecord) bodyPartsObj[mostPart]--
            else return bodyParts
            // 如果最多的部件已经是move部件了，不再重复进行下面move部件的删除
            if (mostPart === 'move') {
                moveNumber--
                continue
            }
            otherNumber--
        }
        // 同时删除move部件，至少留一个move部件
        if (moveNumber > 1 && otherNumber / (moveNumber - 1) <= ratio) {
            const move_index = bodyParts.indexOf('move')
            if (move_index > -1) {
                bodyParts.splice(move_index, 1)
                const movePartRecord = bodyPartsObj['move']
                if (movePartRecord) bodyPartsObj['move']--
                moveNumber--
            }
        }
    }
    return bodyParts
}

/**
 * 获取爬的工作状态
 * @param resourceType 资源类型
 * @param ratio 资源比例
 */
export const getCreepWorking = (creep: Creep, ratio = 1) => {
    if (creep.memory.working && creep.store.getUsedCapacity() == 0) {
        creep.memory.working = false
    }
    if (!creep.spawning && creep.store.getUsedCapacity() >= creep.store.getCapacity() * ratio) {
        creep.memory.working = true
    }
}
